package com.heima.audit.DFA;

import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 敏感词过滤类，使用DFA（Deterministic Finite Automaton）算法进行敏感词检测
 */
@Component
public class SensitiveWordFilter {

    // DFA的状态转移表
    private Map<Integer, Map<Character, Integer>> transitionTable;
    // 终止状态集合
    private final Set<Integer> finalStates;
    // 失败指针
    private Map<Integer, Integer> failPointers;

    /**
     * 构造函数，初始化DFA的状态转移表、终止状态集合和失败指针
     */
    public SensitiveWordFilter() {
        transitionTable = new HashMap<>();
        finalStates = new HashSet<>();
        failPointers = new HashMap<>();
    }

    /**
     * 构建DFA
     *
     * @param sensitiveWords 敏感词列表，用于构建DFA
     */
    public void buildDFA(List<String> sensitiveWords) {
        // 初始化当前状态变量，用于跟踪敏感词的匹配状态
        int currentState = 0;

        // 遍历敏感词列表，构建状态机
        for (String word : sensitiveWords) {
            // 初始化每个敏感词的起始状态
            int state = 0;

            // 遍历敏感词中的每个字符，构建状态转移表
            for (char c : word.toCharArray()) {
                // 如果当前状态的转移表不存在，则创建一个新的转移表
                if (!transitionTable.containsKey(state)) {
                    transitionTable.put(state, new HashMap<>());
                }

                // 如果当前状态的转移表中不包含当前字符，则添加新的状态转移规则
                if (!transitionTable.get(state).containsKey(c)) {
                    int nextState = transitionTable.size();
                    transitionTable.get(state).put(c, nextState);
                }

                // 更新当前状态，为处理下一个字符做准备
                state = transitionTable.get(state).get(c);
            }

            // 将最后一个状态添加到最终状态集合中，表示一个敏感词的完整匹配
            finalStates.add(state);
        }

        // 构建失败指针
        buildFailPointers();

    }

    /**
     * 构建失败指针，用于快速跳转到可以匹配的子状态
     */
    public void buildFailPointers() {
        // 初始化一个队列，用于辅助构建失败指针
        Queue<Integer> queue = new LinkedList<>();

        // 初始化状态0的失败指针指向自身
        failPointers.put(0, 0);

        // 遍历状态0的所有直接后继状态，并初始化它们的失败指针指向状态0
        if (transitionTable.containsKey(0)) {
            for (Map.Entry<Character, Integer> entry : transitionTable.get(0).entrySet()) {
                int nextState = entry.getValue();
                failPointers.put(nextState, 0);
                queue.offer(nextState);
            }
        }

        // 当队列不为空时，循环处理每个状态的失败指针
        while (!queue.isEmpty()) {
            int state = queue.poll();

            // 遍历当前状态的所有直接后继状态
            if (transitionTable.containsKey(state)) {
                for (Map.Entry<Character, Integer> entry : transitionTable.get(state).entrySet()) {
                    char c = entry.getKey();
                    int nextState = entry.getValue();
                    int failState = failPointers.get(state);

                    // 优化点：使用临时变量存储当前字符对应的转移状态，避免多次重复查找
                    Map<Character, Integer> currentCharTransitions = transitionTable.get(failState);
                    while (failState != 0 && (currentCharTransitions == null || !currentCharTransitions.containsKey(c))) {
                        failState = failPointers.get(failState);
                        currentCharTransitions = transitionTable.get(failState);
                    }

                    // 如果找到与当前字符c匹配的转移，则更新失败状态
                    if (currentCharTransitions != null && currentCharTransitions.containsKey(c)) {
                        failState = currentCharTransitions.get(c);
                    }

                    // 更新当前后继状态的失败指针，并将其加入队列以供后续处理
                    failPointers.put(nextState, failState);
                    queue.offer(nextState);
                }
            }
        }
    }

    /**
     * 检测文本是否包含敏感词
     *
     * @param text 待检测的文本
     * @return 如果文本包含敏感词，则返回true；否则返回false
     */
    public boolean isSensitiveWord(String text) {
        // 初始化当前状态为0，表示尚未匹配到任何模式
        int currentState = 0;

        // 遍历输入文本中的每个字符
        for (char c : text.toCharArray()) {
            // 当当前状态非0且当前状态无法通过字符c进行有效转换时，回退到失败指针指向的状态
            while (currentState != 0 && !transitionTable.get(currentState).containsKey(c)) {
                currentState = failPointers.get(currentState);
            }

            // 如果当前状态可以通过字符c进行有效转换，则移动到下一个状态
            if (transitionTable.get(currentState).containsKey(c)) {
                currentState = transitionTable.get(currentState).get(c);
            } else {
                // 如果无法进行有效转换，重置当前状态为0
                currentState = 0;
            }

            // 如果当前状态是接受状态（匹配成功），返回true
            if (finalStates.contains(currentState)) {
                return true;
            }
        }

        // 如果遍历完所有字符后没有匹配成功，返回false
        return false;
    }
}
